from block import Block
import hashlib
import json
import time
import socket
from utils import block_chain_to_json

# 创世区块
genesis_block = Block(
    0,
    "0",
    1650809300,
    "This_is_The_Genesis_Block",
    "b83398af1ae072b3bc24d4e9b4aa18806cc33946859d301521dedf4b7aa29fe5",
    0,
    0,
    "0.0.0.0")

# 挖矿难度
# 5为系数， e后面的数为科学计数法10的次方
bits = "6e72"
bits_target = int(bits.split("e")[0]) * (10 ** int(bits.split("e")[1]))


def get_host_ip():
    """
    查询本机ip地址
    :return:
    """
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(('8.8.8.8', 80))
    ip = s.getsockname()[0]
    s.close()
    return ip


# 本机ip
miner_ip = get_host_ip()


def calculate_hash(block):
    """
    计算区块hash
    :param block: 要计算的区块
    :return: 返回计算后的64位hash值
    """

    # 计算index + previousHash + timestamp + data + bits + nonce的哈希值
    data_str = str(block.index) + block.previous_hash + str(block.timestamp) + block.data + str(block.bits) + str(
        block.nonce)
    # 使用sha256进行计算
    data_hash = hashlib.sha256(data_str.encode('utf8')).hexdigest()
    return data_hash


def is_valid_block(block, pre_block):
    """
    检查区块是否合法
    :param block: 当前区块
    :param pre_block: 前一个区块
    :return : true or false
    """

    # 索引是否正确
    if pre_block.index + 1 != block.index:
        return False

    # 区块前一个hash是否对应
    if pre_block.data_hash != block.previous_hash:
        return False

    # data计算后的hash是否正确
    if calculate_hash(block) != block.data_hash:
        return False

    # 无误返回
    return True


def is_valid_chain(block_chain):
    """
    检查区块链是否合法
    :param block_chain:
    :return: true or false
    """

    # 循环检查
    for index, block in enumerate(block_chain):
        if index > 0:
            # 与前一个区块校验
            if not is_valid_block(block, block_chain[index - 1]):
                print("区块不合法")
                return False

    return True


class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, bytes):
            return str(obj, encoding='utf-8')
        if isinstance(obj, int):
            return int(obj)
        elif isinstance(obj, float):
            return float(obj)
        # elif isinstance(obj, array):
        #    return obj.tolist()
        else:
            return super(MyEncoder, self).default(obj)


def add_block(block, cb):
    """
    添加区块
    :param block: 新区块
    :param cb: 回调函数
    :return: void
    """

    # 获取最新的区块链
    block_chain = get_block_chain()

    # 获取最后一个区块
    block_pre = block_chain[-1]
    # 校验新区块是否合法
    if not is_valid_block(block, block_pre):
        cb(False)
        return

    # 合法，加到区块链中
    block_chain.append(block)

    # 转为字典列表
    block_chain_json = block_chain_to_json(block_chain)

    # 写文件
    # 光标回到起始位置
    file = open('static/blocks.json', 'w', encoding='utf-8')
    file.seek(0, 0)
    file.write(block_chain_json)
    file.flush()
    file.close()
    cb(block)


def generate_next_block(block_data, nonce):
    """
    产生下一个区块
    :param block_data: 区块内容
    :param nonce: nonce
    :return: 生成的区块
    """

    # 获取最新的区块链
    block_chain = get_block_chain()
    # 获取最后一个区块
    last_block = block_chain[-1]

    # 新区块信息
    next_block = Block()
    next_block.index = last_block.index + 1
    next_block.previous_hash = last_block.data_hash
    next_block.timestamp = int(time.time()) / 1000
    next_block.data = block_data
    next_block.bits = bits
    next_block.nonce = nonce
    next_block.data_hash = calculate_hash(next_block)
    next_block.miner_ip = miner_ip
    return next_block


def get_block_chain():
    """
    获取最新的区块链列表
    :return: 最新的区块链列表
    """
    # 读取区块文件
    file = open('static/blocks.json', 'r+', encoding='utf-8')
    # 去除所有空格
    block_chain = file.read().replace(" ", "")
    # 如果长度为空，先写入创世区块
    if block_chain == "":
        block_chain = [genesis_block]
    else:
        block_chain = list(json.loads(block_chain))
        # 转为对象形式
        block_chain = list(map(lambda x: Block(**x), block_chain))
    return block_chain


print(is_valid_chain(get_block_chain()))